package com.wwg.widget.impl;

import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Rect;
import android.util.AttributeSet;
import android.util.Log;
import android.util.Pair;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewParent;
import android.widget.FrameLayout;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;

import com.wwg.widget.IFolderOperate;
import com.wwg.widget.R;
import com.wwg.widget.ViewType;
import com.wwg.widget.fake.FakeBox;
import com.wwg.widget.fake.FakeBoxView;
import com.wwg.widget.fake.op.IFakeMaster;

public class SpringBoardView extends FrameLayout implements IFolderOperate, IFakeMaster {
    private static final String TAG = "SpringBoardView";

    private final SpringBoardContentView mContentView;
    private Adapter mContentAdapter;

    private final SpringBoardFolderContainer mFolderContainer;
    private Adapter mFolderAdapter;

    @Nullable
    private FakeBox mPuppet;

    private int mLastX;
    private int mLastY;
    private boolean mShouldWarning = false;

    private boolean mEntered = false;

    @Nullable
    private SpringBoardContentView mLastActiveContent;

    public SpringBoardView(@NonNull Context context) {
        this(context, null);
    }

    public SpringBoardView(@NonNull Context context, @Nullable AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public SpringBoardView(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        this(context, attrs, defStyleAttr, 0);
    }

    public SpringBoardView(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes) {
        super(context, attrs, defStyleAttr, defStyleRes);

        TypedArray typedArray = context.obtainStyledAttributes(
                attrs,
                R.styleable.SpringBoardView,
                0,
                defStyleRes);
        int folderBG = typedArray.getColor(R.styleable.SpringBoardView_folderBackground, Color.TRANSPARENT);
        typedArray.recycle();

        View inflate = LayoutInflater.from(context).inflate(
                R.layout.spring_board,
                this,
                true);
        mContentView = inflate.findViewById(R.id.board_content);
        mContentView.setFolderOPListener(this);
        if (mContentAdapter != null) {
            mContentView.setAdapter(mContentAdapter);
        }

        mFolderContainer = new SpringBoardFolderContainer(context);
        mFolderContainer.setBackgroundColor(folderBG);
        mFolderContainer.findViewById(R.id.folder_container).setOnClickListener(v -> closeFolder());
        mFolderContainer.findViewById(R.id.folder_content).setOnClickListener(v -> closeFolder());
        if (mFolderAdapter != null) {
            mFolderContainer.setAdapter(mFolderAdapter);
        }
    }

    @Override
    protected void onLayout(boolean changed, int l, int t, int r, int b) {
        // first layout content
        int paddingStart = getPaddingStart();
        int paddingEnd = getPaddingEnd();
        int paddingTop = getPaddingTop();
        int paddingBottom = getPaddingBottom();

        layoutChild(mContentView, paddingStart, paddingTop, paddingEnd, paddingBottom);
        layoutChild(mFolderContainer, paddingStart, paddingTop, paddingEnd, paddingBottom);

        if (mPuppet != null) {
            View puppetView = mPuppet.view;
            if (puppetView.getMeasuredWidth() <= 0 || puppetView.getMeasuredHeight() <= 0) {
                return;
            }

            puppetView.layout(
                    puppetView.getLeft(),
                    puppetView.getTop(),
                    puppetView.getRight(),
                    puppetView.getBottom());
        }
    }

    void layoutChild(View child, int l, int t, int r, int b) {
        if (child == null) {
            return;
        }

        int measuredWidth = child.getMeasuredWidth();
        int measuredHeight = child.getMeasuredHeight();
        if (measuredWidth <= 0 || measuredHeight <= 0) {
            return;
        }

        int left = l;
        int top = t;
        int right = measuredWidth - r;
        int bottom = measuredHeight - b;
        LayoutParams lp = (LayoutParams) child.getLayoutParams();
        if (lp != null) {
            left += lp.getMarginStart();
            top += lp.topMargin;
            right -= lp.getMarginEnd();
            bottom -= lp.bottomMargin;
        }

        child.layout(left, top, right, bottom);
    }

    public void setAdapter(@NonNull Adapter contentAdapter, @NonNull Adapter folderAdapter) {
        mContentAdapter = contentAdapter;
        if (mContentView != null) {
            mContentView.setAdapter(contentAdapter);
        }

        mFolderAdapter = folderAdapter;
        if (mFolderContainer != null) {
            mFolderContainer.setAdapter(folderAdapter);
        }
    }

    void layoutFolderView(@NonNull SpringBoardFolderContainer folderView,
                          int parentMeasuredWidth,
                          int parentMeasuredHeight) {

        int childMeasuredWidth = folderView.getMeasuredWidth();
        int childMeasuredHeight = folderView.getMeasuredHeight();

        int left = (int) ((parentMeasuredWidth - childMeasuredWidth) * 1.f / 2);
        int top = (int) ((parentMeasuredHeight - childMeasuredHeight) * 1.f / 2);
        folderView.layout(left, top, left + childMeasuredWidth, top + childMeasuredHeight);
    }

    boolean closeFolder() {
        SpringBoardFolderContainer folderView = mFolderContainer;
        if (folderView == null || folderView.getParent() == null || folderView.isOperating()) {
            return false;
        }

        Log.i(TAG, "onFolderStartClose");
        folderView.animateHideContent(this::onFolderClosed);

        return true;
    }

    public boolean handleBackPress() {
        SpringBoardFolderContainer folderView = mFolderContainer;
        if (folderView == null) {
            return false;
        }

        if (folderView.isOperating()) {
            return true;
        }

        if (folderView.getParent() == null) {
            return false;
        }

        Log.i(TAG, "onFolderStartClose");
        folderView.animateHideContent(this::onFolderClosed);

        return true;
    }

    void onFolderClosed() {
        if (mPuppet != null && mContentAdapter != null) {
            if (mContentAdapter.onItemInserted(mPuppet.data)) {
                mContentView.insertCurrentBySB();
                mContentView.requestLayout();
            }
        }

        ViewParent parent = getParent();
        if (parent instanceof ViewGroup) {
            ViewGroup group = (ViewGroup) parent;
            int count = group.getChildCount();
            for (int index = 0; index < count; index++) {
                View child = group.getChildAt(index);
                if (child != this) {
                    child.setVisibility(VISIBLE);
                }
            }
        }

        mContentView.onFolderClosed();
    }

    @Override
    public void openFolder(Rect rect) {
        SpringBoardFolderContainer folderView = mFolderContainer;
        if (folderView == null || folderView.getParent() != null || folderView.isOperating()) {
            return;
        }

        mContentView.onTouchEventEnd(false);

        folderView.setAlpha(1);
        folderView.setVisibility(VISIBLE);

        ViewParent parent = getParent();
        if (parent instanceof ViewGroup) {
            ViewGroup group = (ViewGroup) parent;
            int count = group.getChildCount();
            for (int index = 0; index < count; index++) {
                View child = group.getChildAt(index);
                if (child != this) {
                    child.setVisibility(INVISIBLE);
                }
            }
        }

        addViewInLayout(folderView, -1, generateDefaultLayoutParams(), true);

        int parentMeasuredWidth = getMeasuredWidth();
        int parentMeasuredHeight = getMeasuredHeight();
        int widthMeasureSpec = MeasureSpec.makeMeasureSpec(parentMeasuredWidth, MeasureSpec.UNSPECIFIED);
        int heightMeasureSpec = MeasureSpec.makeMeasureSpec(parentMeasuredHeight, MeasureSpec.UNSPECIFIED);
        folderView.setMeasureAllChildren(true);
        folderView.measure(widthMeasureSpec, heightMeasureSpec);
        layoutFolderView(folderView, parentMeasuredWidth, parentMeasuredHeight);

        bringFakeToFront(folderView);

        folderView.animateShowContent(rect, this::onFolderOpened);
        updateBackground();
        mEntered = false;
    }

    void onFolderOpened() {
        if (mFolderContainer != null && mContentView != null) {
            mContentView.releaseDraggable();
            mFolderContainer.onFolderShow(mPuppet);
        }
    }

    @Override
    public boolean hasFake() {
        return mPuppet != null;
    }

    @Override
    public void feedbackFake(SpringBoardContentView.ViewHolder puppet, Object data) {
        FakeBox old = mPuppet;
        if (old != null) {
            View oldView = old.view;
            ViewParent oldFakeParent = oldView.getParent();
            if (oldFakeParent == this) {
                removeView(oldView);
            }
        }

        if (puppet != null) {
            View itemView = puppet.itemView;
            itemView.setDrawingCacheEnabled(true);
            itemView.setDrawingCacheQuality(DRAWING_CACHE_QUALITY_HIGH);
            itemView.setDrawingCacheBackgroundColor(Color.TRANSPARENT);

            int width = itemView.getWidth();
            int height = itemView.getHeight();
            Bitmap bitmap = Bitmap.createBitmap(
                    width,
                    height,
                    Bitmap.Config.ARGB_8888);
            itemView.draw(new Canvas(bitmap));

            FakeBoxView newFake = new FakeBoxView(getContext());
            newFake.setScaleX(itemView.getScaleX());
            newFake.setScaleY(itemView.getScaleY());
            LayoutParams lp = new LayoutParams(
                    LayoutParams.WRAP_CONTENT,
                    LayoutParams.WRAP_CONTENT);
            addViewInLayout(newFake, -1, lp, true);

            int widthMeasureSpec = MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY);
            int heightMeasureSpec = MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY);
            newFake.measure(widthMeasureSpec, heightMeasureSpec);

            Pair<Integer, Integer> offset = getOffsetRelativeToSpringBoard(itemView);
            int left = offset.first;
            int right = left + width;
            int top = offset.second;
            int bottom = top + height;
            newFake.layout(left, top, right, bottom);

            newFake.drawBitmap(bitmap);

            mPuppet = new FakeBox(newFake, puppet.type == ViewType.FOLDER, data);

            itemView.setScaleX(1.f);
            itemView.setScaleY(1.f);
            itemView.setVisibility(View.INVISIBLE);

            mLastActiveContent = findCurrentContent();
        } else {
            mPuppet = null;
            mLastActiveContent = null;
        }
    }

    Pair<Integer, Integer> getOffsetRelativeToSpringBoard(View itemView) {
        float x = itemView.getX();
        float y = itemView.getY();
        ViewParent parent = itemView.getParent();
        while (parent != this) {
            if (parent == null) {
                break;
            }

            x += ((View) parent).getX();
            y += ((View) parent).getY();

            parent = parent.getParent();
        }

        return new Pair<>((int) x, (int) y);
    }

    private void bringFakeToFront(SpringBoardFolderContainer folderView) {
        bringChildToFront(folderView);
        if (mPuppet != null) {
            mPuppet.view.bringToFront();
//            View puppetView = mPuppet.view;
//            puppetView.setZ(Float.MAX_VALUE);
        }
    }

    @Override
    public boolean dispatchTouchEvent(MotionEvent event) {
        FakeBox puppet = mPuppet;
        boolean interceptTouchEvent = puppet != null;

        if (interceptTouchEvent) {
            SpringBoardContentView result = findCurrentContent();
            if (mLastActiveContent != null && mLastActiveContent != result) {
                mLastActiveContent.onTouchEventEnd(false);
            }

            mLastActiveContent = result;
        }

        int action = event.getActionMasked();
        int x = (int) event.getRawX();
        int y = (int) event.getRawY();
        switch (action) {
            case MotionEvent.ACTION_DOWN:
                mLastX = x;
                mLastY = y;
                mShouldWarning = true;
                break;
            case MotionEvent.ACTION_MOVE:
                if (interceptTouchEvent) {
                    int[] position = dragPuppet(x - mLastX, y - mLastY);

                    boolean isIn = transitMonitor(mPuppet.view);
                    if (!isIn && mEntered && mLastActiveContent != mContentView) {
                        boolean canMove = mContentAdapter.canMoveToOutside(mShouldWarning);
                        if (mContentAdapter != null && canMove) {
                            closeFolder();
                        }

                        if (!canMove) {
                            mShouldWarning = false;
                        }
                    }

                    if (mLastActiveContent != null && !mFolderContainer.isOperating()) {
                        mLastActiveContent.onFakeDragged(position[0], position[1]);
                    }
                }
                mLastX = x;
                mLastY = y;
                break;
            case MotionEvent.ACTION_UP:
            case MotionEvent.ACTION_CANCEL:
                mShouldWarning = false;
                mPuppet = null;
                if (mLastActiveContent != null) {
                    mLastActiveContent.springBackFake(puppet);
                }

                if (interceptTouchEvent) {
                    mFolderContainer.endTouchEvent(true);
                    mContentView.onTouchEventEnd(true);
                }

                mLastX = mLastY = -1;
                mFolderContainer.setBoundaryVisibility(INVISIBLE);
                break;

            default:
                break;
        }

        if (interceptTouchEvent) {
            return true;
        }

        return super.dispatchTouchEvent(event);
    }

    private SpringBoardContentView findCurrentContent() {
        SpringBoardContentView result = getFolderContent();
        if (result == null) {
            result = mContentView;
        }

        return result;
    }

    @Nullable
    private SpringBoardContentView getFolderContent() {
        if (mFolderContainer != null && mFolderContainer.getParent() == this) {
            return mFolderContainer.getContent();
        }

        return null;
    }

    int[] dragPuppet(int deltaX, int deltaY) {
        if (mPuppet == null) {
            return new int[] {-1, -1};
        }

        View puppetView = mPuppet.view;
//        Log.i(TAG, "dragCurrentTarget deltaX " + deltaX + " deltaY " + deltaY);
        float newTX = puppetView.getTranslationX() + deltaX;
        float newTY = puppetView.getTranslationY() + deltaY;
        puppetView.setTranslationX(newTX);
        puppetView.setTranslationY(newTY);

        return new int[] {(int) puppetView.getX(), (int) puppetView.getY()};
    }

    /**
     * 过境检查，检查 puppet 是否在 folder content 里面
     * @param puppetView 检查对象
     * @return true means in, false otherwise
     */
    private boolean transitMonitor(View puppetView) {
        SpringBoardContentView folderContent = getFolderContent();
        if (folderContent == null) {
            return false;
        }

        mFolderContainer.setBoundaryVisibility(VISIBLE);

        int[] contentPos = new int[2];
        folderContent.getLocationOnScreen(contentPos);
        Rect contentRect = new Rect(
                contentPos[0],
                contentPos[1],
                contentPos[0] + folderContent.getWidth(),
                contentPos[1] + folderContent.getHeight());

        int[] puppetPos = new int[2];
        puppetView.getLocationOnScreen(puppetPos);
        Rect puppetRect = new Rect(puppetPos[0],
                puppetPos[1],
                puppetPos[0] + puppetView.getWidth(),
                puppetPos[1] + puppetView.getHeight());

        if ((puppetRect.left >= contentRect.left) &&
                (puppetRect.top >= contentRect.top) &&
                (puppetRect.right <= contentRect.right) &&
                (puppetRect.bottom <= contentRect.bottom)) {
            mEntered = true;
        }

        return (puppetRect.left < contentRect.right) &&
                (puppetRect.top < contentRect.bottom) &&
                (puppetRect.right > contentRect.left) &&
                (puppetRect.bottom > contentRect.top);
    }

    private void updateBackground() {
        int width = mContentView.getWidth();
        int height = mContentView.getHeight();
        Bitmap bitmap = Bitmap.createBitmap(
                width,
                height,
                Bitmap.Config.ARGB_8888);
        mContentView.draw(new Canvas(bitmap));

//        RenderScriptHelper.renderScriptBlur(getContext(), bitmap);
//        RenderScriptHelper.doBlur(bitmap, 400, true);
//        mFolderView.setBackground(new BitmapDrawable(getResources(), bitmap));
//        Glide.with(getContext()).load(bitmap)
//                .transform(new BlurTransformation(getContext(), 200))
//                .into(new CustomViewTarget<View, Drawable>(mFolderView) {
//                    public void onLoadFailed(@Nullable Drawable errorDrawable) {}
//                    protected void onResourceCleared(@Nullable Drawable placeholder) {}
//
//                    @Override
//                    public void onResourceReady(@NonNull Drawable resource, @Nullable Transition<? super Drawable> transition) {
//                        mFolderView.setBackground(resource);
//                    }
//                });
    }

    public boolean onBackPressed() {
        return closeFolder();
    }

    public static abstract class Adapter<VH extends ViewHolder>
            extends SpringBoardContentView.Adapter<VH> {}

    public static abstract class ViewHolder extends SpringBoardContentView.ViewHolder {
        public ViewHolder(@NonNull View view) {
            super(view);
        }
    }

}
